require 'fileutils'
require 'logger'
require 'yaml'

module Depac
  class Log
    #making the 'new' method private. so no body can create a instance of Log class by calling .new
    private_class_method :new
    @@logger = nil
    @@logFileAbsolutePath = nil
    @@teeMode = nil
    @@logFilterLevel = nil
    
    #create method will call "new" method only if it is being called first.
    def Log.create(commandName,startTime,notify=false)
      new(commandName,startTime,notify) unless @@logger
    end
   
    # Initialize the input config file.
    def initialize(commandName, startTime, notify)
    
      #First check whether user has the log property file in his home/depac-deployment directory
      logConfigFile = "#{File.expand_path(Depac::CloudDeploymentConstants::LOGGING_PROPERTY_FILEPATH)}"
      
      #if user does not provide the log property file then use the default log property file provided in gem itself
      if !(File.exist?(logConfigFile))
        spec = Gem::Specification.find_by_name(Depac::CloudDeploymentConstants::GEM_NAME)
        gem_root = spec.gem_dir
        logConfigFile = File.expand_path(gem_root + Depac::CloudDeploymentConstants::DEFAULT_LOGGING_PROPERTY_FILEPATH)
      end
      
      logFileDirectory = nil
      if (File.exist?(logConfigFile))
        puts "Loading log configuration from file: #{logConfigFile}"
        logPropertyYMLObject = YAML.load_file(logConfigFile)
        @@logFilterLevel = logPropertyYMLObject['LOGLEVEL']
        logFileDirectory = logPropertyYMLObject['LOGDIR']
        @@teeMode = logPropertyYMLObject['TEE_MODE']
      end
      
      logtgt = nil
      if (logFileDirectory)
        #Expanding the log file directory
        logFileDirectory = "#{File.expand_path(logFileDirectory)}"
        FileUtils.mkdir_p "#{logFileDirectory}"
        
        timeStamp = startTime.strftime("%Y%m%d%H%M%S")
        logFileName = "#{commandName}_#{timeStamp}.log"
        
        @@logFileAbsolutePath = "#{logFileDirectory}/#{logFileName}"
        log_file = File.open("#{logFileDirectory}/#{logFileName}", "a")
        
        if (@@teeMode)
          logtgt = MultiIO.new(STDOUT, log_file)
        else 
          logtgt = log_file
        end
      else
        @@logFileAbsolutePath = STDOUT
        logtgt = MultiIO.new(STDOUT) # Wrapping it around multi-io to ensure flush after every line
      end
      
      @@logger = Logger.new logtgt
      
      #Setting up the log filter level
      if !(@@logFilterLevel); puts "Setting loglevel to INFO"; logFilterLevel="INFO"; end
      changeLogFilterLevel(@@logFilterLevel)
      
      puts "For detailed log info, please see the log file = #{Log.getLogFilePath}" if notify
    
      rescue => error_reason
      puts "ERROR:#{error_reason}"
      exit
    end
  
    def Log.getLogger()
      return @@logger
    end
    
    def Log.getLogFilePath()
      if (@@logFileAbsolutePath == STDOUT) then return 'STDOUT'
      else return @@logFileAbsolutePath
      end
    end
    
    def Log.write(logLevel, msg)
      case logLevel
        when 'UNKNOWN'
          @@logger.unknown(msg)
        when 'FATAL'
          @@logger.fatal(msg)
        when 'ERROR'
          @@logger.error(msg)
        when 'WARN'
          @@logger.warn(msg)
        when 'INFO'
          @@logger.info(msg)
        when 'DEBUG'
          @@logger.debug(msg)
      end
    end
  
    def Log.fatal_exit(msg, exitcode=1)
      fatal(msg)
      exit exitcode
    end
    
    def Log.fatal(msg) 
      @@logger.fatal(msg)
    end
    def Log.error(msg) 
      @@logger.error(msg)
    end
    def Log.warn(msg) 
      @@logger.warn(msg)
    end
    def Log.info(msg) 
      @@logger.info(msg)
    end
    def Log.debug(msg) 
      @@logger.debug(msg)
    end
    
    def Log.close()
      @@logger.close
    end
    
    def changeLogFilterLevel(logFilterLevel)
      case logFilterLevel
        when 'UNKNOWN'
          @@logger.level = Logger::UNKNOWN
        when 'FATAL'
          @@logger.level = Logger::FATAL
        when 'ERROR'
          @@logger.level = Logger::ERROR
        when 'WARN'
          @@logger.level = Logger::WARN
        when 'INFO'
          @@logger.level = Logger::INFO
        when 'DEBUG'
          @@logger.level = Logger::DEBUG
      end
    end
  end
  
  class MultiIO
    def initialize(*targets)
       @targets = targets
    end
  
    def write(*args)
      @targets.each {|t| t.write(*args); t.flush}
    end
  
    def close
      @targets.each(&:close)
    end
  end
end